<?php
/**
 * Created by PhpStorm.
 * User: 牛赞赞
 * Date: 2021/1/20
 * Time: 17:32
 */

namespace App\Task;

use App\Game\Core\DdzPoker;
use App\Game\Core\Packet;
use App\Game\Conf\MainCmd;
use App\Game\Conf\SubCmd;
use Psr\Container\ContainerInterface;
use Swoole\WebSocket\Server as WebSocketServer;

class CallMasterTimerTask
{
    /**
     * @var ContainerInterface
     */
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function callMasterTimer($room_no,$next_chair_id,$call_catch,$times,$call_timer):void{
        $user_conf = config('user');
        $game_conf = config('game');
        $call_loop_key  = sprintf($game_conf['user_room_call'],$room_no);
        $room_data_key  = sprintf($game_conf['user_room_data'],$room_no);
        $room_chair_key = sprintf($game_conf['user_room_chair'],$room_no);

        $this->setCallerLoop($room_no,$next_chair_id,$call_timer);// 设置loop

        // 检查是否机器人
        $isRobot = $this->isRobot($room_no,$next_chair_id);
        if($isRobot['code'] == 1){
            // 机器人默认叫牌
            sleep(rand(3,5));
            $this->doCall($room_data_key,$room_chair_key,$room_no,$times,$call_timer,$isRobot['account'],$call_catch,$next_chair_id);
        }else{
            // 等待
            sleep($user_conf['call_timer']);
            $isLoop = redis()->hGet($call_loop_key,(string)$call_timer);
            if(!empty($isLoop) && ($isLoop == $next_chair_id)){
                $this->doCall($room_data_key,$room_chair_key,$room_no,$times,$call_timer,$isRobot['account'],$call_catch,$next_chair_id);
            }
        }
    }

    /**
     * 叫地主
     * @param $room_data_key
     * @param $room_chair_key
     * @param $room_no
     * @param $times
     * @param $call_timer
     * @param $account
     * @param $call_catch
     * @param $next_chair_id
     */
    protected function doCall($room_data_key,$room_chair_key,$room_no,$times,$call_timer,$account,$call_catch,$next_chair_id){
        $room_data = redis()->hGetAll($room_data_key);
        $call_arr = json_decode($room_data['call_order']);
        $chair_users = json_decode(redis()->get($room_chair_key),true);//房间用户座位号
        if(!isset($room_data['master'])){
            $serv = server();
            $fds = $this->getOtherFdsByRoomNo($room_no);// 房间玩家fds
            $user_info = json_decode($room_data['uinfo'],true);

            // 更新call_times
            $this->updateRoomData($room_no,['call_times'=>$times]);
            $next_time = $times+1;

            // 计数加1
            $call_times_num = $call_timer+1;
            $this->updateCallTimes($room_no,$call_times_num);

            // 更新用户自动叫地主记录
            $this->modifyCallData($room_data,$account,$call_catch);

            // 广播自动叫牌数据
            $this->broadcastRoom($serv,$fds,$next_chair_id,$call_catch,$user_info[$account]['rate']);

            //清除计时器
            $this->cleanCallLoop($room_no,$call_timer);

            // 判断地主产生否
            if($times < 3){
                // 下家叫地主
                $call_order = json_decode($room_data['call_order'],true);
                $next_account = $this->findNextAccount($call_order,$account);
                if($call_catch == 1){
                    $next_call_catch = 1;
                }else{
                    $next_call_catch = 2;
                }
                $chair_id = $this->getChairIdByAccount($room_no,$next_account);
                $task = container()->get(CallNextTask::class);
                $task->callNext($room_no,$chair_id,$next_call_catch,$next_time,$call_times_num);
            }elseif($times == 3){
                $this->third($room_no,$call_arr,$serv,$fds,$chair_users);
            }elseif($times == 4){
                $this->fourth($room_no,$call_arr,$serv,$fds);
            }
        }
    }

    /**
     * 计数叫牌计时器
     * @param $room_no
     * @param $chair_id
     * @param $times
     */
    protected function setCallerLoop($room_no,$chair_id,$times){
        $game_conf = config('game');
        $call_loop_key = sprintf($game_conf['user_room_call'],$room_no);
        redis()->hSet($call_loop_key,(string)$times,$chair_id);
    }

    /**
     * @param $room_no
     * @param $times
     */
    protected function updateCallTimes($room_no,$times){
        $game_conf = config('game');
        $user_room_callTimes = sprintf($game_conf['user_room_callTimes'],$room_no);
        redis()->set($user_room_callTimes, $times);
    }

    /**
     * 清空定时器数据
     * @param $room_no
     * @param $key
     */
    protected function cleanCallLoop($room_no,$key=''){
        $game_conf = config('game');
        $call_loop_key = sprintf($game_conf['user_room_call'],$room_no);
        if(empty($key)){
            redis()->del($call_loop_key);
        }else{
            redis()->hDel($call_loop_key,(string)$key);
        }
    }

    /**
     * 清空定时器数据
     * @param $room_no
     * @param $key
     */
    protected function cleanCallTimes($room_no){
        $game_conf = config('game');
        $room_loop_key = sprintf($game_conf['user_room_callTimes'],$room_no);
        redis()->del($room_loop_key);
    }

    /**
     * 检查是否是机器人
     * @param $room_no
     * @param $chair_id
     * @return array
     */
    protected function isRobot($room_no,$chair_id){
        $game_conf       = config('game');
        $room_data_key   = sprintf($game_conf['user_room_data'],$room_no);
        $room_chair_key  = sprintf($game_conf['user_room_chair'],$room_no);
        $room_data       = redis()->hGetAll($room_data_key);
        $room_user_chair = json_decode(redis()->get($room_chair_key),true);
        $account = array_search($chair_id,$room_user_chair);
        $user_data    = json_decode($room_data[$account],true);
        if(($user_data['is_robot'] == 1) || ($user_data['take_over'] == 1)){
            $msg = ['code'=>1,'account'=>$account];
        }else{
            $msg = ['code'=>0,'account'=>$account];
        }
        return $msg;
    }

    /**
     * 查找下一家account
     * @param $call_arr
     * @param $account
     * @return mixed
     */
    protected function findNextAccount($call_arr,$account){
        $curr_no = array_search($account,$call_arr);
        if(($curr_no+1)>2){
            $next_user_account = $call_arr[0];
        }else{
            $next_user_account = $call_arr[$curr_no+1];
        }
        return $next_user_account;
    }

    /**
     * 获取房间其他用户的fds
     * @param $room_no
     * @return array
     */
    protected function getOtherFdsByRoomNo($room_no){
        $game_conf = config('game');
        $room_wait_key = sprintf($game_conf['user_room_wait'],$room_no);
        $room_users = json_decode(redis()->get($room_wait_key),true);
        $fds = [];
        foreach ($room_users as $k=>$v){
            $bind_key = sprintf($game_conf['user_bind_key'],$v);
            $fds[] = redis()->get($bind_key);
        }
        return $fds;
    }

    /**
     * @param $room_no
     * @param $chair_id
     * @return int|string
     */
    public function getAccountByChairId($room_no,$chair_id){
        $game_conf = config('game');
        $room_chair_key = sprintf($game_conf['user_room_chair'],$room_no);
        $chair_ids = json_decode(redis()->get($room_chair_key),true);
        $account = '';
        foreach ($chair_ids as $k=>$v){
            if($chair_id == $v){
                $account = $k;
                break;
            }
        }
        return $account;
    }

    /**
     * @param $room_no
     * @param $account
     * @return int|string
     */
    public function getChairIdByAccount($room_no,$account){
        $game_conf = config('game');
        $room_chair_key = sprintf($game_conf['user_room_chair'],$room_no);
        $chair_ids = json_decode(redis()->get($room_chair_key),true);
        return $chair_ids[$account];
    }

    /**
     * 更新叫地主数据
     * @param $room_data
     * @param $account
     * @param $call_catch
     */
    protected function modifyCallData($room_data,$account,$call_catch){
        $user_room_data = json_decode($room_data[$account],true);
        foreach ($user_room_data as $key=>$val){
            $user_room_data['call_catch'] = $call_catch;
            $user_room_data['val'] = 0;
        }
        $params[$account] = json_encode($user_room_data);
        $this->updateRoomData($room_data['room_no'],$params);
    }

    /**
     * 批量更新hash
     * @param $room_no
     * @param $params
     */
    protected function updateRoomData($room_no,$params){
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        foreach ($params as $key=>$val){
            redis()->hSet($room_data_key, (string)$key, $val);
        }
    }

    /**
     * @param $serv
     * @param $fds
     * @param $chair_id
     * @param $call_catch
     * @param $new_rate
     */
    protected function broadcastRoom($serv,$fds,$chair_id,$call_catch,$new_rate){
        $msg = array('chair_id'=>$chair_id,'rate'=>$new_rate,'call_or_catch'=>$call_catch,'call_catch_val'=>0);
        $send_data = Packet::packFormat('OK', 0, $msg);
        $send_data = Packet::packEncode($send_data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_CALL_RESP);
        foreach($fds as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $send_data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * @param $room_no
     * @param $call_arr
     * @param $serv
     * @param $fds
     * @param $chair_users
     */
    protected function third($room_no,$call_arr,$serv,$fds,$chair_users){
        $redis = redis();
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        if(redis()->exists($room_data_key)){
            $caller1 = json_decode($redis->hGet($room_data_key,(string)$call_arr[0]),true);
            $caller2 = json_decode($redis->hGet($room_data_key,(string)$call_arr[1]),true);
            $caller_times_num = $this->getCallerTimes($room_no);
            if(($caller1['call_catch'] == 1) && ($caller1['val'] == 0)){
                if(($caller2['call_catch'] == 1) && ($caller2['val'] == 0)){
                    // 清除准备状态
                    $this->cleanRoomPrepare($game_conf,$room_no);
                    // 清除房间数据
                    $this->cleanRoomData($game_conf,$room_no);
                    // 房间重新发牌次数
                    //投递异步发牌任务
                    $task = container()->get(GameSyncTask::class);
                    $task->assignCards($room_no);
                }
                if(($caller2['call_catch'] == 1) && ($caller2['val'] == 1)){
                    // 第二家地主
                    $this->masterData($call_arr[1],$room_no,$serv,$fds,false);
                }
            }
            if(($caller1['call_catch'] == 1) && ($caller1['val'] == 1)){
                if(($caller2['call_catch'] == 2) && ($caller2['val'] == 0)){
                    // 第一家地主
                    $this->masterData($call_arr[0],$room_no,$serv,$fds,false);
                }
                if(($caller2['call_catch'] == 2) && ($caller2['val'] == 1)){
                    // 第一家再次下发抢地主
                    $task = container()->get(CallNextTask::class);
                    $task->callNext($room_no,$chair_users[$call_arr[0]],2,4,$caller_times_num+1);
                }
            }
        }
    }

    /**
     * 第四次计时触发
     * @param $room_no
     * @param $call_arr
     * @param $serv
     * @param $fds
     */
    protected function fourth($room_no,$call_arr,$serv,$fds){
        $redis = redis();
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        if(redis()->exists($room_data_key)){
            $caller1 = json_decode($redis->hGet($room_data_key,(string)$call_arr[0]),true);
            $caller2 = json_decode($redis->hGet($room_data_key,(string)$call_arr[1]),true);
            $caller3 = json_decode($redis->hGet($room_data_key,(string)$call_arr[2]),true);
            if(($caller1['call_catch'] == 2) && ($caller1['val'] == 0)){
                if(($caller2['call_catch'] == 2) && ($caller2['val'] == 1)){
                    // 第二家地主
                    $this->masterData($call_arr[1],$room_no,$serv,$fds,false);
                }
                if(($caller2['call_catch'] == 2) && ($caller2['val'] == 0) && ($caller3['call_catch'] == 2) && ($caller3['val'] == 1)){
                    // 第三家地主
                    $this->masterData($call_arr[2],$room_no,$serv,$fds,false);
                }
            }
            if(($caller2['call_catch'] == 2) && ($caller2['val'] == 0)){
                // 第三家地主
                $this->masterData($call_arr[2],$room_no,$serv,$fds,false);
            }
        }
    }

    /**计时器序号
     * @param $room_no
     * @return int
     */
    protected function getCallerTimes($room_no){
        $game_conf = config('game');
        $user_room_callTimes = sprintf($game_conf['user_room_callTimes'],$room_no);
        if(redis()->exists($user_room_callTimes)){
            $times = redis()->get($user_room_callTimes);
        }else{
            $times = 1;
        }
        return (int)$times;
    }

    /**
     * 地主数据
     * @param $account
     * @param $room_no
     * @param $serv
     * @param $fds
     * @param $rate
     */
    protected function masterData($account,$room_no,$serv,$fds,$rate){
        //抓牌,合并手牌数据
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        $room_data = redis()->hGetAll($room_data_key);
        $user_data = json_decode($room_data[$account], true);
        $hand = json_decode($room_data['hand'], true);
        $card = array_values(array_merge($user_data['card'], $hand));
        $obj_ddz = new DdzPoker();
        $card = $obj_ddz->_sortCardByGrade($card);
        $user_data['card'] = $card;

        //设置地主和用户手牌数据
        $param = array(
            'master'=>$account,
            $account=>json_encode($user_data)
        );

        // 整体翻倍
        $uinfo     = json_decode($room_data['uinfo'],true);
        if($rate){
            foreach ($uinfo as $k=>$v){
                $uinfo[$k]['rate'] = ($uinfo[$k]['rate']*2);
            }
        }
        $update_data['uinfo'] = json_encode($uinfo);

        $this->updateRoomData($room_no,$update_data);
        $this->updateRoomData($room_no, $param);
        $this->catchGameCard($room_data, $account,$serv,$fds);
    }

    /**
     * 抓牌返回数据
     * @param $room_data
     * @param $user
     * @param $serv
     * @param $fds
     */
    protected function catchGameCard($room_data,$user,$serv,$fds)
    {
        $user_conf = config('user');
        $info = json_decode($room_data[$user], true);
        $msg = array(
            'account'=>$user,
            'chair_id' => $info['chair_id'],
            'hand_card' => json_decode($room_data['hand'],true)
        );
        $data = Packet::packFormat('OK', 0, $msg);
        $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_CATCH_BASECARD_RESP);
        $this->pushToUsers($serv, $fds, $data);

        //清除计时器
        $this->cleanCallLoop($room_data['room_no']);

        // 清除叫牌计时器
        $this->cleanCallTimes($room_data['room_no']);

        usleep($user_conf['send_play_timer']);

        // 推下家出牌广播
        $param = [
            'next_chair_id'=>$info['chair_id'],   //下一个出牌的椅子id
            'wait_time'=>$user_conf['play_timer']
        ];
        $data_next = Packet::packFormat('OK', 1, $param);
        $data_next = Packet::packEncode($data_next, MainCmd::CMD_SYS, SubCmd::SUB_GAME_OUT_CARD_LOOP_RESP);
        $this->pushToUsers($serv,$fds, $data_next);

        // 投递出牌计时任务
        $task = container()->get(OutCardTimerTask::class);
        $task->outCardTimer($room_data['room_no'],$info['chair_id'],1);
    }

    /**
     * 对多用发送信息
     * @param $serv
     * @param $users
     * @param $data
     */
    protected function pushToUsers($serv, $users, $data)
    {
        foreach($users as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * 清除房间准备记录
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanRoomPrepare($game_conf,$room_no){
        $room_prepare_key = sprintf($game_conf['user_room_prepare'],$room_no);
        redis()->del($room_prepare_key);
    }

    /**
     * 清除房间游戏数据
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanRoomData($game_conf,$room_no){
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        redis()->del($room_data_key);
    }

    /**
     * 获取重新发牌次数
     * @param $game_conf
     * @param $room_no
     * @return bool|mixed|string
     */
    protected function getAssignTimes($game_conf,$room_no){
        $room_assign_key = sprintf($game_conf['user_room_assign'],$room_no);
        if(redis()->exists($room_assign_key)){
            $times = redis()->get($room_assign_key);
        }else{
            $times = 1;
        }
        return $times;
    }

    /**
     * 设置重新发牌次数
     * @param $game_conf
     * @param $room_no
     * @param $times
     */
    protected function setAssignTimes($game_conf,$room_no,$times){
        $room_assign_key = sprintf($game_conf['user_room_assign'],$room_no);
        $new_times = $times+1;
        redis()->set($room_assign_key,$new_times,86400);
    }

    /**
     * 清除重新发牌次数计数
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanAssignTimes($game_conf,$room_no){
        $room_assign_key = sprintf($game_conf['user_room_assign'],$room_no);
        redis()->del($room_assign_key);
    }

    /**
     * 清除房间玩家座位号记录
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanRoomChair($game_conf,$room_no){
        $room_chair_key = sprintf($game_conf['user_room_chair'],$room_no);
        if(redis()->exists($room_chair_key)){
            redis()->del($room_chair_key);
        }
    }

    /**
     * 清除房间号
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanRoomNo($game_conf,$room_no){
        $user_room_total_key    = $game_conf['user_room_total'];
        redis()->sRem($user_room_total_key,$room_no);
    }

    /**
     * 清除map
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanRoomMap($game_conf,$room_no){
        $room_wait_key = sprintf($game_conf['user_room_wait'],$room_no);
        if(redis()->exists($room_wait_key)){
            $user = json_decode(redis()->get($room_wait_key),true);
            foreach ($user as $k=>$v){
                $user_map_key = sprintf($game_conf['user_room'],$v);
                redis()->del($user_map_key);
            }
        }
    }

    /**
     * 清除房间等待绑定的玩家账号
     * @param $game_conf
     * @param $room_no
     */
    protected function cleanRoomWait($game_conf,$room_no){
        $room_wait_key = sprintf($game_conf['user_room_wait'],$room_no);
        if(redis()->exists($room_wait_key)){
            redis()->del($room_wait_key);
        }
    }
}